package chips440.internals;

import java.io.*;
import java.util.Scanner;

import chips440.tiles.*;
import chips440.main.Types;

/**
 * This class parses an ASCII text file describing a game map into a
 * meaningful format in memory. It does not perform robust syntax checking on
 * the specified file; if there's an error it will simply crash.
 *
 * @author Ian Voysey
 * @version 0.1
 */
public class BoardParser{
    /* simple tiles */
    private static final char WALL = '*';
    private static final char NORM = ' ';

    /* fundamental tile types */
    private static final char H2O  = 'h';
    private static final char FIRE = 'f';
    private static final char ICEY = 'i';

    /* four types of movement tiles*/
    private static final char MOVE_U = '^';
    private static final char MOVE_D = 'v';
    private static final char MOVE_L = '<';
    private static final char MOVE_R = '>';

    /* the different types of boots */
    private static final char W_BOOT = 'H';
    private static final char F_BOOT = 'F';
    private static final char I_BOOT = 'I';
    private static final char M_BOOT = 'M';

    /* movable dirt blocks -- NOT IMPLEMENTED AT ALL */
    private static final char DIRT = 'D';

    /* the way to end the level */
    private static final char DONE = 'x';

    /* initial player position */
    private static final char PLAYER = 'P';

    /* the different colors of doors */
    private static final char R_DOOR = 'R';
    private static final char Y_DOOR = 'Y';
    private static final char B_DOOR = 'B';
    private static final char G_DOOR = 'G';

    /* the different types of keys */
    private static final char R_KEY = 'r';
    private static final char Y_KEY = 'y';
    private static final char B_KEY = 'b';
    private static final char G_KEY = 'g';

    /* sweet delicious chips */
    private static final char CHIP = 'c';

    /* where to look for boards */
    private static final String DIR = System.getProperty("user.dir")+"/chips440/resources/boards/";

    /**
     * Takes a level number, reads a map file, populates a Board with
     * the data, and returns that board. At this version, this method provides
     * absolutely no map file syntax sanity checking; it will fail
     * semi-mysteriously unless everything goes perfectly.
     *
     * @param map_id the level number of the map to load
     * @throws IllegalArgumentException this is throw if the specified level's
     * file has even vauely incorrect syntax.
     * @throws FileNotFoundException if the specified level doesn't have a file
     */
    public static Board parseLevel(int map_id)
	throws IllegalArgumentException, FileNotFoundException
    {
	Scanner map_scan;
	Board parsed;
	String path_temp = DIR + "lvl" + map_id + ".board";
	try{
	    map_scan = new Scanner(new File(path_temp));
	} catch (FileNotFoundException e){
	    throw new FileNotFoundException("bad level number: " + path_temp);
	}

	int max_time = 0, n = 0, max_chip = 0;
	String next_line;

	/* pull the first few lines of the file to get enough information to
	 * create a new board
	 */
	if(map_scan.hasNextInt()){
	    max_time = map_scan.nextInt();
	} else {
    
    if(map_scan.hasNext())
        System.out.println(map_scan.next());
	    throw new IllegalArgumentException("bad map file synxtax for max time " +
					       path_temp);
	}
	if(map_scan.hasNextInt()){
	    n = map_scan.nextInt();
	} else {
	    throw new IllegalArgumentException("bad map file synxtax for board size " +
					       path_temp);
	}
	if(map_scan.hasNextInt()){
	    max_chip = map_scan.nextInt();
	} else {
	    throw new IllegalArgumentException("bad map file synxtax for max chips " +
					       path_temp);
	}

	parsed = new Board(n,max_time,max_chip);

	/*
	 * now that we have a board to actually populate, we finish off the
	 * file.
	 */
	char read_temp[];
	int col = 0;
	Tile tile_temp = null;

	while(map_scan.hasNextLine()){
	    read_temp = (map_scan.nextLine()).toCharArray();

	    for(int row = 0; row < read_temp.length; row++){
		switch(read_temp[row]) {
		case WALL:
		    tile_temp = new WallTile();
		    break;
		case NORM:
		    tile_temp = null;
		    break;

		/* movable dirt blocks are not implemented yet */
		case DIRT:
		    tile_temp = null;
		    break;

		case H2O:
		    tile_temp = new WaterTile();
		    break;
		case FIRE:
		    tile_temp = new FireTile();
		    break;
		case ICEY:
		    tile_temp = new IceTile();
		    break;
		case MOVE_U:
		    tile_temp = new MoveTile(Types.Direct.UP);
		    break;
		case MOVE_D:
		    tile_temp = new MoveTile(Types.Direct.DOWN);
		    break;
		case MOVE_L:
		    tile_temp = new MoveTile(Types.Direct.LEFT);
		    break;
		case MOVE_R:
		    tile_temp = new MoveTile(Types.Direct.RIGHT);
		    break;

		case DONE:
		    tile_temp = new EndTile();
		    break;

		case PLAYER:
		    parsed.setInitPlayerPos(col,row);
		    continue;

		case R_DOOR:
		    tile_temp = new DoorTile(Types.Item.R_KEY);
		    break;
		case Y_DOOR:
		    tile_temp =new DoorTile(Types.Item.Y_KEY);
		    break;
		case B_DOOR:
		    tile_temp = new DoorTile(Types.Item.B_KEY);
		    break;
		case G_DOOR:
		    tile_temp = new DoorTile(Types.Item.G_KEY);
		    break;

		case R_KEY:
		    tile_temp = new NormalTile(Types.Item.R_KEY);
		    break;
		case Y_KEY:
		    tile_temp = new NormalTile(Types.Item.Y_KEY);
		    break;
		case B_KEY:
		    tile_temp = new NormalTile(Types.Item.B_KEY);
		    break;
		case G_KEY:
		    tile_temp = new NormalTile(Types.Item.G_KEY);
		    break;

		case F_BOOT:
		    tile_temp = new NormalTile(Types.Item.F_BOOT);
		    break;
		case W_BOOT:
		    tile_temp = new NormalTile(Types.Item.W_BOOT);
		    break;
		case M_BOOT:
		    tile_temp = new NormalTile(Types.Item.M_BOOT);
		    break;
		case I_BOOT:
		    tile_temp = new NormalTile(Types.Item.I_BOOT);
		    break;
		case CHIP:
		    tile_temp = new NormalTile(Types.Item.CHIP);
		    break;
		default:
		    throw new IllegalArgumentException
			("body of map file contains illegal character: "
			 + read_temp[row] );
		}
		parsed.setTile(col,row,tile_temp);
	    }
	    col++;
	}
    map_scan.close();
	return parsed;
    }
}
